
#include "HC_SR04.h"

// HC_SR04 超声波测距驱动——基于 STM32F407 开发板

// 捕获状态说明：
// [7]: 0 : 没有成功的捕获; 1 : 成功捕获到一次
// [6]: 0 : 还没捕获到低电平; 1 : 已经捕获到低电平了
// [5:0]:捕获低电平后溢出的次数
// 设置 1us 计数器加 1
// HCSR04 模块精度为 3mm ,范围为 2 cm ~ 4 m 
// 4 m / 340 m/s(声速) = 11764 us ≈ 2^14 = 0x2FFF
// 此处使用的是 16 位定时器,定时器计数值最高为 0xFFFF 满足要求(TIM2,TIM5是32位的!!)

u8 Left_Sta = 0;  //输入捕获状态——左方
u16 Left_Val = 0; //输入捕获值——左方

u8 Front_Sta = 0;  //输入捕获状态——前方
u16 Front_Val = 0; //输入捕获值——前方

/* 
 * 功能:测量左侧距离
 * 返回:length —— 左侧距离 ( cm )
 */
int Get_Left_Length(void)
{
    int i = 0;
    int sum = 0;
    while (i < 5)//五次测量取平均，滤波
    {
        int time = 0;
        GPIO_SetBits(LEFT_PORT, LEFT_TRIG); //给与一个 20us 的下降沿
        delay_us(20);
        GPIO_ResetBits(LEFT_PORT, LEFT_TRIG);
        if ((Left_Sta & 0X80))//成功捕获到了一次高电平
        {
            time = Left_Sta & 0X3F;
            time *= 0XFFFF;   //溢出时间总和
            time += Left_Val; //得到总的高电平时间
            Left_Sta = 0;     //开启下一次捕获
            sum += time / 58; // 声速 340 m/s /2 = 170 m/s = 17 X 10^(-3) cm/us = 0.017 cm/us 
            ++i;              // time X 声速(0.017) == time / 58
        }
    }
    int length = sum / 5;
    length = (length > 400) ? 400 : length; //传感器精度上限为 4 m
    return length;
}

/* 
 * 功能:测量前方距离
 * 返回:length —— 前方距离 ( cm )
 */
int Get_Front_Length(void)
{
    int i = 0;
    int sum = 0;
    while (i < 5)//五次测量取平均，滤波
    {
        int time = 0;
        GPIO_SetBits(FRONT_PORT, FRONT_TRIG); //给与一个 20us 的下降沿
        delay_us(20);
        GPIO_ResetBits(FRONT_PORT, FRONT_TRIG); 
        if ((Front_Sta & 0X80))//成功捕获到了一次高电平
        {
            time = Front_Sta & 0X3F;
            time *= 0XFFFF;    //溢出时间总和
            time += Front_Val; //得到总的高电平时间
            Front_Sta = 0;     //开启下一次捕获
            sum += time / 58;  // us · m/s => us · cm/us = cm
            ++i;
        }
    }
    int length = sum / 5;
    length = (length > 400) ? 400 : length; //传感器精度上限为 4 m
    return length;
}

//定时器 1 中断服务程序
void TIM3_IRQHandler(void)
{
    if ((Front_Sta & 0X80) == 0) //前方——还未成功捕获
    {
        if (TIM_GetITStatus(FRONT_TIM, TIM_IT_Update) != RESET) //定时器更新中断 | 溢出
        {
            if (Front_Sta & 0X40) //已经捕获到高电平了
            {
                if ((Front_Sta & 0X3F) == 0X3F) //高电平太长了
                {
                    Front_Sta |= 0X80; //标记成功捕获了一次
                    Front_Val = 0XFFFF;
                }
                else
                    Front_Sta++;
            }
        }
        if (TIM_GetITStatus(FRONT_TIM, TIM_IT_CC1) != RESET) //发生捕获事件
        {
            if (Front_Sta & 0X40) //捕获到一个下降沿
            {
                Front_Sta |= 0X80;                                       //标记成功捕获到一次高电平脉宽
                Front_Val = TIM_GetCapture1(FRONT_TIM);                  //获取当前的捕获值.
                TIM_OC1PolarityConfig(FRONT_TIM, TIM_ICPolarity_Rising); //CC1P=0 设置为上升沿捕获
            }
            else //还未开始,第一次捕获上升沿
            {
                Front_Sta = 0; //清空
                Front_Val = 0;
                Front_Sta |= 0X40;                                        //标记捕获到了上升沿
                TIM_Cmd(FRONT_TIM, DISABLE);                              //关闭定时器
                TIM_SetCounter(FRONT_TIM, 0);                             //清零
                TIM_OC1PolarityConfig(FRONT_TIM, TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获
                TIM_Cmd(FRONT_TIM, ENABLE);                               //使能定时器
            }
        }
    }
    TIM_ClearITPendingBit(FRONT_TIM, TIM_IT_CC1 | TIM_IT_Update); //清除中断标志位
}

//定时器 4 中断服务程序
void TIM4_IRQHandler(void)
{
    if ((Left_Sta & 0X80) == 0) //左——还未成功捕获
    {
        if (TIM_GetITStatus(LEFT_TIM, TIM_IT_Update) != RESET) //定时器更新中断 | 溢出
        {
            if (Left_Sta & 0X40) //已经捕获到高电平了
            {
                if ((Left_Sta & 0X3F) == 0X3F) //高电平太长了
                {
                    Left_Sta |= 0X80; //标记成功捕获了一次
                    Left_Val = 0XFFFF;
                }
                else
                    Left_Sta++;
            }
        }
        if (TIM_GetITStatus(LEFT_TIM, TIM_IT_CC1) != RESET) //发生捕获事件
        {
            if (Left_Sta & 0X40) //捕获到一个下降沿
            {
                Left_Sta |= 0X80;                                       //标记成功捕获到一次高电平脉宽
                Left_Val = TIM_GetCapture1(LEFT_TIM);                   //获取当前的捕获值.
                TIM_OC1PolarityConfig(LEFT_TIM, TIM_ICPolarity_Rising); //设置为上升沿捕获
            }
            else //还未开始,第一次捕获上升沿
            {
                Left_Sta = 0; //清空
                Left_Val = 0;
                Left_Sta |= 0X40;                                        //标记捕获到了上升沿
                TIM_Cmd(LEFT_TIM, DISABLE);                              //关闭定时器
                TIM_SetCounter(LEFT_TIM, 0);                             //清零
                TIM_OC1PolarityConfig(LEFT_TIM, TIM_ICPolarity_Falling); //CC1P=1 设置为下降沿捕获
                TIM_Cmd(LEFT_TIM, ENABLE);                               //使能定时器
            }
        }
    }
    TIM_ClearITPendingBit(LEFT_TIM, TIM_IT_CC1 | TIM_IT_Update); //清除中断标志位
}

/* 
 * 功能:初始化左侧超声波模块
 */
static void HCSR04_Left_Init(u32 Period, u16 Prescaler)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    //时钟使能
    RCC_APB1PeriphClockCmd(LEFT_TIM_CLK, ENABLE); //时钟使能
    RCC_AHB1PeriphClockCmd(LEFT_CLK, ENABLE);

    //初始化 IO
    GPIO_InitStructure.GPIO_Pin = LEFT_TRIG;      //发送电平引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;   //推挽复用输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //上拉
    GPIO_Init(LEFT_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = LEFT_ECHO;       //返回电平引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;   //复用功能//浮空输入
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
    GPIO_Init(LEFT_PORT, &GPIO_InitStructure);

    GPIO_PinAFConfig(LEFT_PORT, LEFT_ECHO_PINSOURCE, LEFT_TIM_AF); // IO 复用

    //配置 TIM 基础定时参数
    TIM_TimeBaseStructure.TIM_Prescaler = Prescaler;            //定时器分频
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
    TIM_TimeBaseStructure.TIM_Period = Period;                  //自动重装载值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(LEFT_TIM, &TIM_TimeBaseStructure);

    //配置 TIM 输入捕获参数
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;                //CC1S=01 	选择输入端 IC1映射到TI1上
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;     //上升沿捕获
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;           //配置输入分频,不分频
    TIM_ICInitStructure.TIM_ICFilter = 0x00;                        //IC1F=0000 配置输入滤波器 不滤波
    TIM_ICInit(LEFT_TIM, &TIM_ICInitStructure);

    TIM_ITConfig(LEFT_TIM, TIM_IT_Update | TIM_IT_CC1, ENABLE); //允许更新中断 ,允许CC1IE捕获中断

    TIM_Cmd(LEFT_TIM, ENABLE); //使能定时器

    //配置 NVIC 中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = LEFT_TIM_IRQ;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;        //子优先级3
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);
}

/* 
 * 功能:初始化前方超声波模块
 */
static void HCSR04_Front_Init(u32 Period, u16 Prescaler)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_ICInitTypeDef TIM_ICInitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;

    //时钟使能
    RCC_APB1PeriphClockCmd(FRONT_TIM_CLK, ENABLE); //时钟使能
    RCC_AHB1PeriphClockCmd(FRONT_CLK, ENABLE);

    //初始化 IO
    GPIO_InitStructure.GPIO_Pin = FRONT_TRIG;     //发送电平引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT; //推挽输出
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;   //推挽复用输出
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //上拉
    GPIO_Init(FRONT_PORT, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = FRONT_ECHO;      //返回电平引脚
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;   //复用功能//浮空输入
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN; //下拉
    GPIO_Init(FRONT_PORT, &GPIO_InitStructure);

    GPIO_PinAFConfig(FRONT_PORT, FRONT_ECHO_PINSOURCE, FRONT_TIM_AF); // IO 复用

    //配置 TIM 基础定时参数
    TIM_TimeBaseStructure.TIM_Prescaler = Prescaler;            //定时器分频
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
    TIM_TimeBaseStructure.TIM_Period = Period;                  //自动重装载值
    TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
    TIM_TimeBaseInit(FRONT_TIM, &TIM_TimeBaseStructure);

    //配置 TIM 输入捕获参数
    TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;                //CC1S=01 	选择输入端 IC1映射到TI1上
    TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;     //上升沿捕获
    TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到TI1上
    TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;           //配置输入分频,不分频
    TIM_ICInitStructure.TIM_ICFilter = 0x00;                        //IC1F=0000 配置输入滤波器 不滤波
    TIM_ICInit(FRONT_TIM, &TIM_ICInitStructure);

    TIM_ITConfig(FRONT_TIM, TIM_IT_Update | TIM_IT_CC1, ENABLE); //允许更新中断 ,允许CC1IE捕获中断

    TIM_Cmd(FRONT_TIM, ENABLE); //使能定时器

    //配置 NVIC 中断优先级
    NVIC_InitStructure.NVIC_IRQChannel = FRONT_TIM_IRQ;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; //抢占优先级1
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;        //子优先级2
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;           //IRQ通道使能
    NVIC_Init(&NVIC_InitStructure);
}

/* 
 * 功能:初始化全部超声波模块
 */
void HCSR04_Init(void)
{
    HCSR04_Left_Init(MAX_Period, HCSR_Prescaler);
    HCSR04_Front_Init(MAX_Period, HCSR_Prescaler);
}
